
#pragma once

// callingPendingFunctors_???

#include <sys/eventfd.h>
#include <atomic>
#include <functional>
#include <mutex>
#include <thread>
#include <vector>

class Epoller;
class Channel;

class EventLoop {
    using Functor = std::function<void()>;

public:
    int CreateWakeupFd();

    // 唤醒 subloop
    void ReadWakeupFd();

    void WriteWakeupFd();

    void DoPendingFunctors();

    bool IsInLoop();

    void RunInLoop(const Functor cb);

    void QueueInLoop(const Functor cb);

public:
    EventLoop();

    // 开启事件循环
    /**
     * 执行当前EventLoop事件循环需要处理的回调操作 对于线程数 >=2 的情况 IO线程 mainloop(mainReactor) 主要工作：
     * accept接收连接 => 将accept返回的connfd打包为Channel =>
     * TcpServer::newConnection通过轮询将TcpConnection对象分配给subloop处理
     *
     * mainloop调用queueInLoop将回调加入subloop（该回调需要subloop执行 但subloop还在poller_->poll处阻塞）
     * queueInLoop通过wakeup将subloop唤醒
     **/
    void Loop();

    // 关闭事件循环
    /**
     * 退出事件循环
     * 1. 如果loop在自己的线程中调用quit成功了 说明当前线程已经执行完毕了loop()函数的poller_->poll并退出
     * 2. 如果不是当前EventLoop所属线程中调用quit退出EventLoop 需要唤醒EventLoop所属线程的epoll_wait
     *
     * 比如在一个subloop(worker)中调用mainloop(IO)的quit时 需要唤醒mainloop(IO)的poller_->poll 让其执行完loop()函数
     *
     * 注意： 正常情况下 mainloop负责请求连接 将回调写入subloop中 通过生产者消费者模型即可实现线程安全的队列
     * 但是muduo通过wakeup()机制 使用eventfd创建的wakeupFd_ notify 使得mainloop和subloop之间能够进行通信
     **/
    void Quit();

    void UpdateChannel(Channel* channel);
    void RemoveChannel(Channel* channel);

    ~EventLoop();

private:
    std::atomic_bool looping_;
    std::atomic_bool quit_;
    std::thread::id thread_id_;
    int wakeup_fd_;
    Epoller* epoller_;
    std::mutex mutex_;
    std::vector<Functor> tasks_;               // mainloop获取到新channel，用来唤醒subloop(轮询算法)
    std::unique_ptr<Channel> wakeup_channel_;  // 绑定 wakeu_fd
};
